package utils

/*
 +----------------------------------------------------------------------
 + Title        : EtcdServiceDiscovery
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2022-01-06
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : etcd 服务发现
 +				: 插件: go.etcd.io/etcd/client/v3
 +----------------------------------------------------------------------
*/
import (
	"context"
	"encoding/json"
	"log"
	"sync"

	clientv3 "go.etcd.io/etcd/client/v3"
)

// 定义ETCD服务发现结构体
type EtcdServiceDiscovery struct {
	client     *clientv3.Client
	lock       sync.Mutex
	services   map[string]ServiceDiscoveryInfo
	watchedKey string
}

// 服务发现信息
type ServiceDiscoveryInfo struct {
	ID          string `json:"id"`           // 唯一实例ID (UUID)
	ServiceName string `json:"service_name"` // 服务名称
	Address     string `json:"address"`      // 实例地址
	Version     string `json:"version"`      // 服务版本
	Weight      int8   `json:"weight"`       // 权重
	Status      string `json:"status"`       // 健康状态
	Region      string `json:"region"`       // 部署区域
	Zone        string `json:"zone"`         // 部署区域内的分区
	Metadata    string `json:"metadata"`     // 元数据
	LastUpdated int64  `json:"last_updated"` // 最后更新时间
}

/**
 * 创建ETCD服务发现
 *
 * @param []string endpoints ----------------------------------------必传
 * @param string watchedKey -----------------------------------------必传
 * @param string username -------------------------------------------必传
 * @param string password -------------------------------------------必传
 * @return *EtcdServiceDiscovery, error
 * @author huwl
 */
func NewEtcdServiceDiscovery(endpoints []string, username, password, watchedKey string) (*EtcdServiceDiscovery, error) {
	client, err := clientv3.New(clientv3.Config{
		Endpoints: endpoints,
		Username:  username,
		Password:  password,
	})
	if err != nil {
		return nil, err
	}

	return &EtcdServiceDiscovery{
		client:     client,
		lock:       sync.Mutex{},
		services:   make(map[string]ServiceDiscoveryInfo),
		watchedKey: watchedKey,
	}, nil
}

/**
 * 发现服务
 *
 * @return error
 * @author huwl
 */
func (sd *EtcdServiceDiscovery) DiscoverServices() error {
	resp, err := sd.client.Get(context.Background(), sd.watchedKey, clientv3.WithPrefix())
	if err != nil {
		return err
	}

	for _, kv := range resp.Kvs {
		sd.lock.Lock()
		defer sd.lock.Unlock()
		sdi := ServiceDiscoveryInfo{}
		json.Unmarshal(kv.Value, &sdi)
		sd.services[string(kv.Key)] = sdi
	}
	return nil
}

/**
 * 监听服务
 *
 * @return error
 * @author huwl
 */
func (sd *EtcdServiceDiscovery) WatchServices() error {
	watchChan := sd.client.Watch(context.Background(), sd.watchedKey, clientv3.WithPrefix())
	for watchResp := range watchChan {
		for _, event := range watchResp.Events {
			switch event.Type {
			case clientv3.EventTypePut:
				sd.handlePutEvent(event)
			case clientv3.EventTypeDelete:
				sd.handleDeleteEvent(event)
			}
		}
	}

	return nil
}

/**
 * 处理新增或者修改事件
 *
 * @author huwl
 */
func (sd *EtcdServiceDiscovery) handlePutEvent(event *clientv3.Event) {
	sd.lock.Lock()
	defer sd.lock.Unlock()

	serviceName := string(event.Kv.Key)
	sdi := ServiceDiscoveryInfo{}
	json.Unmarshal(event.Kv.Value, &sdi)

	sd.services[serviceName] = sdi
	log.Printf("Added service: %s -> %s\n", serviceName, sdi.Address)
}

/**
 * 处理删除事件
 *
 * @author huwl
 */
func (sd *EtcdServiceDiscovery) handleDeleteEvent(event *clientv3.Event) {
	sd.lock.Lock()
	defer sd.lock.Unlock()

	serviceName := string(event.Kv.Key)

	delete(sd.services, serviceName)
	log.Printf("Removed service: %s\n", serviceName)
}
